import { copyFileSync, existsSync, readdirSync, readFileSync, statSync, writeFileSync } from "fs";
import { basename, extname, join } from "path";
import * as zipper from "zip-local";
import { cpDir, cpFile, mkdirsSync } from "../../utils/CpFile";
import { getJsList } from "../../utils/PaserFile";
import { compareVersion } from "../../utils/VerUtil";
import { BasePack } from "../BasePack";
import { IOppoPackageOptions } from "../IPack";
import path = require("path");
import { getTodaytr } from "../../utils/TimeUtil";
import { installCnpm } from "../../utils/ChildProcessUtil";

export class VivoPack extends BasePack {
    protected _packOpt: IOppoPackageOptions;

    constructor(packProjectJsonName: string) {
        super(packProjectJsonName);
        this._projectConfigFile = 'minigame.config.js';
        this._layaEnginePlugins = [];
    }

    protected async _initProject() {

        await installCnpm();

        // let remoteVer = await this.exec('npm view @vivo-minigame/cli version')
        // console.log('remote version:', remoteVer);
        let localVer = await this.exec('mg -v');
        console.log('local version:', localVer);

        if (!localVer || compareVersion(localVer + '', '1.2.1') == -1) {
            // 从外网拉一次
            console.log('installing @vivo-minigame/cli, maybe need 30s, please wait...');
            await this.exec("cnpm i @vivo-minigame/cli -g");
        }
        let outPath = join(this._packOpt.outputPath, `${this._plat}`);
        if (!existsSync(outPath)) {
            mkdirsSync(outPath);
        }
        console.log('init vivogame, maybe need 30s, please wait...');
        await this.exec(`mg init vivogame`, { cwd: outPath });

        this._minigamePath = join(this._packOpt.outputPath, `${this._plat}/${this._plat}game`);

        // 将模板拷贝到vivogame目录中
        let from = this._getTemplateUrl()
        let out = join(this._packOpt.outputPath, `${this._plat}/${this._plat}game`);
        cpDir(from, out, () => {

            this._startPack(null)
        });
    }

    protected _setEngine() {
        let from: string[] = [];
        let to: string[] = [];

        let fromIndex = join(this._packOpt.projectBin, 'index.js');
        let jslist = getJsList(fromIndex);
        jslist.forEach(url => {
            from.push(join(this._packOpt.projectBin, url))
            let libUrl = join(this._minigamePath, `engine/${url}`);
            console.log(libUrl);

            to.push(libUrl);
        })

        cpFile(from, to, () => {

            this._setGameJS(jslist);
            this._setMinigameConfigJs(jslist);

            if (this._packOpt.skipResCheck) {
                console.log(this._mode + ' pack over');
                // 将配置的config拷贝到engine目录中
                this._setGameConfig(join(this._minigamePath, `engine/config.js`));
                this._setSignAndPack();

                return;
            } else {
                this._resPath = this._packOpt.resPath;
                this._cpResIndex = 0;
                // 将配置的config拷贝到engine目录中
                this._setGameConfig(join(this._minigamePath, `engine/config.js`));

                this._cpRes();
            }

        }, !this._packOpt.skipCodeUglify)
    }

    protected _setGameJS(jslist: string[]) {
        // 每次都将模板目录中的game.js拷贝到vivo项目中，然后重新刷新vivo项目中的game.js
        let tempGamejsUrl = join(this._getTemplateUrl(), 'src/game.js');
        let gamejsUrl = join(this._minigamePath, 'src/game.js');
        copyFileSync(tempGamejsUrl, gamejsUrl);
        console.log('reset game.js');

        // 将src中的game.js修改导入文件
        let gameStr = readFileSync(gamejsUrl).toString();
        let requireStr = '';

        jslist.forEach((js, i) => {
            if (js.indexOf('bundle.js') >= 0 || i == jslist.length - 1) {
                // requireStr += `require("./qgame-adapter.js");\n`;
                // requireStr += `require("./libs/laya.vvmini.js");\n`;
                requireStr += `require("./config.js");\n`;
            }
            requireStr += `require("./${js}");\n`;
        })
        gameStr = gameStr.replace('/** REQUIRE_HOLD */', requireStr);
        writeFileSync(gamejsUrl, gameStr);
    }

    protected _setMinigameConfigJs(jslist: string[]) {
        // 将engine里面的js,配置到minigame.config.js中
        let configStr = 'const externals = [\n';

        jslist.forEach((js, i) => {
            if (js.indexOf('bundle.js') >= 0 || i == jslist.length - 1) {
                configStr += this._addExternal('qgame-adapter.js') + ',\n';
                // configStr += this._addExternal('libs/laya.vvmini.js') + ',\n';
                configStr += this._addExternal('config.js') + ',\n';
            }
            configStr += this._addExternal(js) + (i < jslist.length - 1 ? ',\n' : '\n');
        })
        configStr += ']\n'
        let mcfgUrl = join(this._minigamePath, 'minigame.config.js');
        let mcfgStr = readFileSync(mcfgUrl).toString();
        mcfgStr = mcfgStr.replace(/const externals = \[([^*].|\n|\r)*\]/g, configStr);
        writeFileSync(mcfgUrl, mcfgStr);
    }
    protected _addExternal(url: string) {
        let str = '{\n';
        str += `module_name: './${url}',\n`;
        str += `module_path: './${url}',\n`;
        str += `module_from: 'engine/${url}',\n`;
        str += '}'
        return str;
    }

    protected _cpRes() {
        if (this._cpResIndex >= this._resPath.length) {
            this._setSignAndPack();
            return;
        }
        let pathName = this._resPath[this._cpResIndex]
        let toResUrl = join(this._minigamePath, 'src', pathName);
        if (this._packOpt.resVer) {
            toResUrl = join(this._packOpt.outputPath, `${this._plat}/${this._packOpt.resVer}/${pathName}`);
        }
        let tempList = pathName.split('/');
        if (tempList[tempList.length - 1].indexOf('.') >= 0) {
            // 是文件
            mkdirsSync(path.dirname(toResUrl))
            copyFileSync(join(this._packOpt.projectBin, pathName), toResUrl);
            console.log(this._mode + ' cp file about: ' + pathName);
            this._cpResIndex++;
            this._cpRes()
        } else {
            // 是目录
            cpDir(join(this._packOpt.projectBin, pathName), toResUrl, () => {
                console.log(this._mode + ' cp dir about: ' + pathName);
                this._cpResIndex++;
                this._cpRes()
            })
        }
    }

    protected _setSign(cb?: () => void) {
        // prod的模式下,需要拷贝正式签名
        if (!this._packOpt.signPath || this._mode == 'dev') {
            if (!!cb) cb();
            return;
        }
        let from: string[] = [
            join(this._packOpt.signPath, 'certificate.pem'),
            join(this._packOpt.signPath, 'private.pem')
        ];
        let to: string[] = [
            join(this._minigamePath, 'sign/release/certificate.pem'),
            join(this._minigamePath, 'sign/release/private.pem')
        ];
        cpFile(from, to, () => {
            console.log('release sign ok');
            if (!!cb) cb();
        });
    }

    protected _setSignAndPack() {
        this._setSign(() => {

            this._pack();
        })
    }

    protected _startPack(packOpt: IOppoPackageOptions) {
        this._minigamePath = join(this._packOpt.outputPath, `${this._plat}/${this._plat}game`);
        console.log('reset manifest.json');
        let manifestUrl = join(this._minigamePath, `src/manifest.json`);
        let manifest = JSON.parse(readFileSync(manifestUrl).toString());
        manifest.package = this._packOpt.packageId;
        manifest.name = this._packOpt.gameName;
        manifest.versionName = this._packOpt.versionName;
        manifest.versionCode = this._packOpt.versionCode + '';
        manifest.minPlatformVersion = this._packOpt.minPlatformVersion + '';
        manifest.deviceOrientation = this._packOpt.orientation;
        manifest.thirdEngine = this._packOpt.thirdEngine;
        let icomName = basename(join(this._packOpt.iconPath));
        manifest.icon = `/image/${icomName}`;

        writeFileSync(manifestUrl, JSON.stringify(manifest));

        console.log('reset logo');
        // 将logo拷贝到导出目录
        let fromIconUrl = join(this._packOpt.iconPath);
        let outIconUrl = join(this._minigamePath, `src/image`, basename(fromIconUrl));
        copyFileSync(fromIconUrl, outIconUrl);

        // 将配置的config拷贝到engine目录中
        // this._setGameConfig(join(this._minigamePath, `engine/config.js`));
        // 拷贝完毕了 qgame-adapter.js 后,需要将所需要的lib和游戏主代码从bin中拷贝到engine目录中
        this._setEngine();
    }

    protected async _pack() {
        if (this._mode == 'dev') {
            await this.exec('npm run build', { cwd: this._minigamePath })
            this._preview()
        } else if (this._mode == 'prod') {
            console.log(`当前打包版本为:\x1b[92m ${this._packOpt.versionName} \x1b[97m,请注意！`);

            await this.exec('npm run release', { cwd: this._minigamePath })
            this._zipRpk()
            this._packOver()
        }
    }

    protected _preview() {
        // dev 的模式下，才有预览
        let reg = new RegExp(/((?:http:\/\/)(?:.[\w]+)+)/g);
        let cp = this.spawn('npm.cmd', ['run', 'server'], { cwd: this._minigamePath }, (log) => {
            // console.log('preview',log);
            let result = reg.exec(log);
            if (result) {
                // console.log('result:', result);
                this.exec(`start ${result[0]}`);
            }
            this._packOver()
        })
    }

    protected _zipRpk() {
        let distName = join(this._minigamePath, 'dist')
        let files = readdirSync(distName);
        for (const ifile of files) {
            let ifurl = path.join(distName, ifile);
            if (statSync(ifurl).isFile() && ifurl.indexOf('.rpk') >= 0) {
                // 如果是.rpk文件
                let ext = extname(ifurl);
                // console.log('base',path.dirname(ifurl));

                if (ifurl.substr(ifurl.length - ext.length, ext.length) == ext) {
                    // let newUrlHead = ifurl.substr(0, ifurl.length - ext.length);
                    // let newUrl = `${newUrlHead}_${this._packOpt.versionName}${extname(ifurl)}`;
                    // renameSync(ifurl, newUrl);
                    let newName = `${this._plat}_${this._packOpt.gameName}_${getTodaytr()}_${this._packOpt.versionName}.zip`;
                    let newUrl = join(path.dirname(ifurl), newName);
                    zipper.sync.zip(ifurl).compress().save(newUrl);

                }
            }
        }
    }
}